package com.dlc.felear.xiaoensale.utils;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.dlc.felear.xiaoensale.constant.Constant;
import com.dlc.felear.xiaoensale.dlc_serial_port.DlcSerialPortClient;
import com.dlc.felear.xiaoensale.entity.StepMotorInfo;
import com.dlc.felear.xiaoensale.factory.CmdList;
import com.dlc.felear.xiaoensale.helper.JsonHelper;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class SendJbCmdUtil implements Handler.Callback {
    private static final String TAG = "SendJbCmdUtil";
    private static final int waitDelay = 100;
    public static final int SPEED_L = 300;
    public static final int SPEED_M = 180;
    public static final int SPEED_H = 250;

    private ScheduledExecutorService scheduledThreadPool;
    private ArrayBlockingQueue<byte[]> mQueue;
    private int currentRunSpeed = 0;
    private int currentPlus =0;
    private int addr = 1;
    private String SerialNum = Constant.TTYS0;
    private int lastSpeed = 0;
    private int preSetSpeed = 200;
    private boolean isRun_360;
    private boolean yaobai = false;
    private long lastTimeRecord = 0;
    private int lastJsd = 1;
    private Map<String, Integer> cmdsRecord;
    private int flagCount = 0;
    private boolean xgStatus=false;
    private boolean cgStatus=false;

    public SendJbCmdUtil(ScheduledExecutorService scheduledThreadPool) {
        this.scheduledThreadPool = scheduledThreadPool;
        mQueue = new ArrayBlockingQueue<>(10);
        cmdsRecord = new HashMap<>();
        loopSendCmd();
        initMotor();
        timer();
    }

    public void setBooleanValue(boolean isRun_360, boolean yaobai,boolean xgStatus, boolean cgStatus) {
        this.isRun_360 = isRun_360;
        this.yaobai = yaobai;
        this.xgStatus = xgStatus;
        this.cgStatus = cgStatus;
    }

    public void setlastTimeRecord(long lastTimeRecord) {
        this.lastTimeRecord = lastTimeRecord;
    }

    public void initMotor() {
        addCmd(CmdList.stepMotorStop(addr));
        addCmd(CmdList.stepMotorAcceleration1(addr, 5));
        addCmd(CmdList.stepMotorAcceleration2(addr, 5));
        setMotorSpeed((byte) 1);
        addCmd(CmdList.stepMotorPul(addr, 0));
    }

    @Override
    public boolean handleMessage(Message message) {
        return false;
    }

    public void setMotorStop() {
        log("步进电机停止", 0);
        mQueue.clear();
        addCmd(CmdList.stepMotorStop(addr));
    }


    /**
     * 设置步进电机脉冲
     *
     * @param plus
     */
    public void setMotorPlus(int plus) {
        log("步进电机设置脉冲:" + plus, 0);
        if(plus!=this.currentPlus){
            addCmd(CmdList.stepMotorPul(addr, plus));
        }
    }

    /**
     * 设置步进电机速度
     *
     * @param leve
     */
    public void setMotorSpeed(byte leve) {
        log("步进电机设置速度：" + getAngleByLevel(leve), 0);
        preSetSpeed = getAngleByLevel(leve);
        if (getAngleByLevel(leve) == currentRunSpeed) {
            log("设置的速度和当前运行速度一致,忽略该指令", 0);
            return;
        }
        setJsd(getAcceleration(leve));
        addCmd(CmdList.stepMotorSpeed(addr, getAngleByLevel(leve)));
    }

    /**
     * 设置电机往返动作
     *
     * @param plus
     * @param derect
     */
    public void setMotorRepet(int plus, int derect) {
        log("步进电机设置往返功能,脉冲:" + plus, 0);
        addCmd(CmdList.stepMotorPul(addr, plus));
        addCmd(CmdList.stepMotorReRun(addr, derect));
    }

    /**
     * 设置电机启动时的加速度
     *
     * @param value
     */
    private void setJsd(int value) {
        log("步进电机设置加速度:" + getAcceleration((byte) value), 0);
        if (value != lastJsd) {
            lastJsd = value;
            addCmd(CmdList.stepMotorAcceleration1(addr, value));
        }
    }

    /**
     * 电机正转
     */
    public void zhengzhuanCmd() {
        log("步进电机正转", 0);
        addCmd(CmdList.stepMotorZz(addr));
    }

    /**
     * 电机反转
     */
    public void fanzhuanCmd() {
        log("步进电机反转", 0);
        addCmd(CmdList.stepMotorFz(addr));
    }

    /**
     * 指令回复信息
     *
     * @param data
     */
    public void reviceData(byte[] data) {
            log("指令回复:" + HexUtil.bytes2HexString(data), 0);
        if (CmdList.checkDataComplete(data)) {
            if (data[1] == (byte) 6) {
                removeCmds(data);
                resultInfoData(data);
            } else if (data[1] == (byte) 3) {
                queryInfoData(data);
            }
        }
    }

    /**
     * 查询参数指令返回
     *
     * @param data
     */
    private void queryInfoData(byte[] data) {
        switch (data[2]) {
            case (byte) 0x06:
                int speed = (int) HexUtil.hex2decimal(HexUtil.encodeHexStr(Arrays.copyOfRange(data, 3, 5)));
                if (speed > 0) {
                    currentRunSpeed = speed;
                }
                recoverZeroSpeed(speed);
                int plus1 = (int) HexUtil.hex2decimal(HexUtil.encodeHexStr(Arrays.copyOfRange(data, 7, 9)));
                currentPlus = plus1;
                Log.e(TAG, "脉冲数:" + plus1 + " speed=" + speed);
                break;
        }
    }

    /**
     * 设置指令信息回复
     *
     * @param data
     */
    private void resultInfoData(byte[] data) {
        switch (data[3]) {
            case (byte) 0x05:
                int currentRunSpeed1 = (int) HexUtil.hex2decimal(HexUtil.encodeHexStr(Arrays.copyOfRange(data, 4, 6)));
                Log.e(TAG, "当前设置速度:" + currentRunSpeed1);
                break;
            case (byte) 0x03:
                break;
            case (byte) 0x02:
                break;
        }
    }

    /**
     * 添加指令到发送队列
     *
     * @param data
     */
    private void addCmd(byte[] data) {
        try {
            if (data != null) {
                mQueue.offer(data);
            }
        } catch (Exception e) {

        }
    }

    /**
     * 发送指令
     */
    private void loopSendCmd() {
        scheduledThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        byte[] orders = null;
                        // take位阻塞方法，取不到消息时会阻塞当前线程
                        while ((orders = mQueue.take()) != null) {
                            sendCmd(orders);
                            putRecordCmd(orders);
                            // 睡眠，防止数据粘包
                            Thread.sleep(waitDelay);
                        }
                    } catch (Exception e) {

                    }
                }
            }
        });
    }

    /**
     * 记录已发送的指令，用于校验指令是否丢失
     *
     * @param data
     */
    public void putRecordCmd(byte[] data) {
//        log("记录指令："+HexUtil.bytes2HexString(data),0);
        if (data[1] != (byte) 0x06) {
            return;
        }
        String tempKey = HexUtil.bytes2HexStringNoSpace(data).trim();
        if (cmdsRecord.containsKey(tempKey)) {
            cmdsRecord.put(tempKey, cmdsRecord.get(tempKey) + 1);
            return;
        }
        cmdsRecord.put(tempKey, 1);
    }

    /**
     * 移除集合信息
     *
     * @param data
     */
    private void removeCmds(byte[] data) {
        if (data[1] != (byte) 0x06) {
            return;
        }
        String tempKey = HexUtil.bytes2HexStringNoSpace(data).trim();
        if (cmdsRecord.containsKey(tempKey)) {
            cmdsRecord.put(tempKey, cmdsRecord.get(tempKey) - 1);
        }
    }

    /**
     * 获取档位对应的速度值
     *
     * @param speed
     * @return
     */
    public int getAngleByLevel(byte speed) {
        log("设置速度档位:" + speed, 0);
        if (speed == (byte) 2) {
            return SPEED_M;
        }
        if (speed == (byte) 3) {
            return SPEED_H;
        }
        return SPEED_L;
    }

    /**
     * 获取不同档位时的加速度
     *
     * @param level
     * @return
     */
    private int getAcceleration(byte level) {
        if (level == 2) {
            return 3;
        }
        if (level == 3) {
            return 2;
        }
        return 5;
    }

    private void log(String msg, int level) {
        Log.e(TAG, msg);
    }

    /**
     * 定时轮询
     */
    private void timer() {
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
//                addCmd(CmdList.queryMotorSpeed(addr));
                rotateCheck();
            }
        }, 0, 1, TimeUnit.SECONDS);
    }

    /**
     * 电机减速
     *
     * @param value
     */
    public void jiangsu(byte value) {
        lastSpeed = preSetSpeed;
        log("降低搅拌速度 lastSpeed=" + lastSpeed, 0);
        setMotorSpeed(value);
    }

    /**
     * 恢复减速前的电机速度
     */
    public void recoverSpeed() {
        log("恢复搅拌速度 lastSpeed=" + lastSpeed, 0);
        addCmd(CmdList.stepMotorSpeed(addr, getAngleByLevel((byte) lastSpeed)));
    }

    /**
     * 步进电机调试
     *
     * @param stepMotorInfo
     */
    public void recieveCmd(StepMotorInfo stepMotorInfo) {
        log("步进电机调试：" + JsonHelper.toJson(stepMotorInfo), 0);
        switch (stepMotorInfo.getFlag()) {
            case 0:
                //设置速度
                addCmd(CmdList.stepMotorSpeed(addr, stepMotorInfo.getValue()));
                break;
            case 1:
                //设置脉冲
                addCmd(CmdList.stepMotorPul(addr, stepMotorInfo.getValue()));
                break;
            case 2:
                //设置加速度
                addCmd(CmdList.stepMotorAcceleration1(addr, stepMotorInfo.getValue()));
                break;
            case 3:
                //设置计数
                addCmd(CmdList.stepMotorAconut(addr, stepMotorInfo.getValue()));
                break;
            case 4:
                //停止
                addCmd(CmdList.stepMotorStop(addr));
                break;
            case 5:
                //正转
                addCmd(CmdList.stepMotorZz(addr));
                break;
            case 6:
                //反转
                addCmd(CmdList.stepMotorFz(addr));
                break;
            case 7:
                //查询加速
                addCmd(CmdList.queryMotorSpeed(addr));
                break;
            case 8:
                //查询脉冲
                addCmd(CmdList.queryMotorPlus(addr));
                break;
            case 9:
                //查询计数
//                addCmd(CmdList.queryMotorAconut(addr));
                addCmd(CmdList.stepMotorReRun(addr, 1));
                break;

        }
    }

    /**
     * 防止搅拌器卡死
     */
    private void rotateCheck() {
//        log("搅拌器距离上一次经过底部的时间:"+(System.currentTimeMillis() - lastTimeRecord),0);
        if ((yaobai || isRun_360) && System.currentTimeMillis() - lastTimeRecord >= 8000) {
            log("搅拌被卡住", 0);
//            if(currentRunSpeed<getAngleByLevel((byte) preSetSpeed)){
//                setMotorSpeed ((byte) preSetSpeed);
//            }
//            fanzhuanCmd();
        }
        if (flagCount % 2 == 0) {
            checkCmdLose();
        }
        flagCount++;
    }

    /**
     * 校验指令是否丢失
     */
    private void checkCmdLose() {
        for (String key : cmdsRecord.keySet()) {
            if (cmdsRecord.get(key) > 0) {
//             log("该指令存在丢失:"+key+" 丢失次数:"+cmdsRecord.get(key),0);
            }
        }
    }

    /**
     * 防止搅拌器在摆动情况下不摆动
     *
     * @param currentRunSpeed
     */
    private void recoverZeroSpeed(int currentRunSpeed) {
        if (isRun_360 && currentRunSpeed <= 0) {
            addCmd(CmdList.stepMotorSpeed(addr, preSetSpeed));
            zhengzhuanCmd();
            log("恢复速度:" + preSetSpeed, 0);
        } else if (yaobai && currentRunSpeed <= 0) {

        }
    }

    /**
     * 发送指令到串口
     *
     * @param data
     */
    private void sendCmd(byte[] data) {
        putRecordCmd(data);
        DlcSerialPortClient.getInstance().send(SerialNum, data);
    }

    /**
     * 获取当前电机速度
     *
     * @return
     */
    public int getCurrentSpeed() {

        return currentRunSpeed;
    }


}
